package cn.lzy.study.datasource.aspect;


import cn.lzy.study.datasource.annotation.DataSource;
import cn.lzy.study.datasource.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 注解@DataSource切面
 * @author LZY
 * @since 2022-08-10
 */
@Component
@Aspect
@Order(10)
public class DataSourceAspect {

    /**
     * 定义切点
     * （若类和其方法上都有 @DataSource，则方法上的优先级高于类上）
     * @annotation(cn.lzy.study.datasource.annotation.DataSource)--表示方法上有注解 @DataSource 就将方法拦截下来
     * @within(cn.lzy.study.datasource.annotation.DataSource)--表示如果类上面有注解 @DataSource 就将该类中的方法拦截下来
     */
    @Pointcut("@annotation(cn.lzy.study.datasource.annotation.DataSource) || @within(cn.lzy.study.datasource.annotation.DataSource)")
    public void pc() {

    }


    @Around("pc()")
    public Object around(ProceedingJoinPoint pjp) {
        // 获取方法上的有效注解
        DataSource dataSouce = getDataSouce(pjp);
        if (dataSouce != null) {
            // 获取注解中的值（即：数据源名称）
            String dataSourceName = dataSouce.value();
            // 将该数据源名称设置到线程中
            DynamicDataSourceContextHolder.setDataSourceType(dataSourceName);
        }
        try {
            // 放行方法（执行真实方法）
            return pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        } finally {
            // 清除数据源
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
        return null;
    }


    /**
     * 获取方法上的注解对象
     * @param pjp
     * @return
     */
    private DataSource getDataSouce(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        // 查找方法上的注解
        DataSource annotation = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (annotation != null) {   // 方法上存在目标注解
            return annotation;
        }
        // 方法上不存在注解，则返回类上的注解（signature.getDeclaringType()--获取方法所在的类型）
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}
